4.8 类型检测与类型转换

4.8.1 is,!is运算符

is运算符可以检查对象是否与特定的类型兼容(“兼容”的意思是:此对象是该类型,或者派生于该类型)。

is运算符用来检查对象(变量)是否属于某数据类型(如Int、String、Boolean等)。C#里面也有这个运算符。

is运算符类似Java的instanceof:

  1. @org.junit.runner.RunWith(org.junit.runners.JUnit4.class)
  2. public class TypeSystemDemo {
  3. @org.junit.Test
  4. public void testVoid() {
  5. if ("abc" instanceof String) {
  6. println("abc is instanceof String");
  7. } else {
  8. println("abc is not instanceof String");
  9. }
  10. }
  11. void println(Object obj) {
  12. System.out.println(obj);
  13. }
  14. }

在Kotlin中,我们可以在运行时通过使用 is 操作符或其否定形式 !is 来检查对象是否符合给定类型:

  1. >>> "abc" is String
  2. true
  3. >>> "abc" !is String
  4. false
  5. >>> null is Any
  6. false
  7. >>> null is Any?
  8. true

代码示例:

  1. @RunWith(JUnit4::class)
  2. class ASOperatorTest {
  3. @Test fun testAS() {
  4. val foo = Foo()
  5. val goo = Goo()
  6. println(foo is Foo) //true 自己
  7. println(goo is Foo)// 子类 is 父类 = true
  8. println(foo is Goo)//父类 is 子类 = false
  9. println(goo is Goo)//true 自己
  10. }
  11. }
  12. open class Foo
  13. class Goo : Foo()

类型自动转换

在Java代码中,当我们使用str instanceof String来判断其值为true的时候,我们想使用str变量,还需要显式的强制转换类型:

  1. @org.junit.runner.RunWith(org.junit.runners.JUnit4.class)
  2. public class TypeSystemDemo {
  3. @org.junit.Test
  4. public void testVoid() {
  5. Object str = "abc";
  6. if (str instanceof String) {
  7. int len = ((String)str).length(); // 显式的强制转换类型为String
  8. println(str + " is instanceof String");
  9. println("Length: " + len);
  10. } else {
  11. println(str + " is not instanceof String");
  12. }
  13. boolean is = "1" instanceof String;
  14. println(is);
  15. }
  16. void println(Object obj) {
  17. System.out.println(obj);
  18. }
  19. }

而大多数情况下,我们不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的 is-检查,并在需要时自动插入(安全的)转换:

  1. @Test fun testIS() {
  2. val len = strlen("abc")
  3. println(len) // 3
  4. val lens = strlen(1)
  5. println(lens) // 1
  6. }
  7. fun strlen(ani: Any): Int {
  8. if (ani is String) {
  9. return ani.length
  10. } else if (ani is Number) {
  11. return ani.toString().length
  12. } else if (ani is Char) {
  13. return 1
  14. } else if (ani is Boolean) {
  15. return 1
  16. }
  17. print("Not A String")
  18. return -1
  19. }

4.8.2 as运算符

as运算符用于执行引用类型的显式类型转换。如果要转换的类型与指定的类型兼容,转换就会成功进行;如果类型不兼容,使用as?运算符就会返回值null。

代码示例:

  1. >>> open class Foo
  2. >>> class Goo:Foo()
  3. >>> val foo = Foo()
  4. >>> val goo = Goo()
  5. >>> foo as Goo
  6. java.lang.ClassCastException: Line69$Foo cannot be cast to Line71$Goo
  7. >>> foo as? Goo
  8. null
  9. >>> goo as Foo
  10. Line71$Goo@73dce0e6

我们可以看出,在Kotlin中,子类是禁止转换为父类型的。

按照Liskov替换原则,父类转换为子类是对OOP的严重违反,不提倡、也不建议。严格来说,父类是不能转换为子类的,子类包含了父类所有的方法和属性,而父类则未必具有和子类同样成员范围,所以这种转换是不被允许的,即便是两个具有父子关系的空类型,也是如此。